package ws

import (
	"fmt"
	"gitee.com/visons/gopkg/logger"
	"github.com/gorilla/websocket"
	"github.com/tomasen/realip"
	"net"
	"net/http"
	"net/url"
)

type Server struct {
	urlStr       string
	listener     net.Listener
	upgrader     websocket.Upgrader
	manager      *Manager
	protocol     Protocol
	handler      Handler
	sendChanSize int

	certfile string
	keyfile  string
}

type Handler interface {
	HandleSession(*Session)
}

var _ Handler = HandlerFunc(nil)

type HandlerFunc func(*Session)

func (f HandlerFunc) HandleSession(session *Session) {
	f(session)
}

func NewServer(urlStr string, protocol Protocol, sendChanSize int, handler Handler) *Server {
	return &Server{
		urlStr: urlStr,
		upgrader: websocket.Upgrader{
			CheckOrigin: func(_ *http.Request) bool {
				return true
			},
		},
		manager:      NewManager(),
		protocol:     protocol,
		handler:      handler,
		sendChanSize: sendChanSize,
	}
}

func (self *Server) SetHandler(handler Handler) {
	self.handler = handler
}

func (self *Server) SetHttps(certfile, keyfile string) {
	self.certfile = certfile
	self.keyfile = keyfile
}

func (self *Server) Serve() error {
	urlObj, err := url.Parse(self.urlStr)
	if err != nil {
		return fmt.Errorf("websocket.urlparse failed(%s) %v", self.urlStr, err)
	}
	if urlObj.Path == "" {
		return fmt.Errorf("websocket serve failed, expect path in url to listen %v", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc(urlObj.Path, func(w http.ResponseWriter, r *http.Request) {
		conn, err := self.upgrader.Upgrade(w, r, nil)
		if err != nil {
			logger.Error("websocket upgrade failed %v", err)
			return
		}
		codec, err := self.protocol.NewCodec(conn)
		if err != nil {
			conn.Close()
			return
		}
		session := self.manager.NewSession(codec, self.sendChanSize)
		session.remoteAddr = realip.FromRequest(r)
		self.handler.HandleSession(session)
	})

	self.listener, err = net.Listen("tcp", urlObj.Host)
	if err != nil {
		return err
	}

	if urlObj.Scheme == "https" {
		return http.ServeTLS(self.listener, mux, self.certfile, self.keyfile)
	} else {
		return http.Serve(self.listener, mux)
	}
}

func (self *Server) GetSession(sessionID uint64) *Session {
	return self.manager.GetSession(sessionID)
}

func (self *Server) Stop() {
	self.listener.Close()
	self.manager.Dispose()
}
